﻿using System;

namespace RuoVea.ExUtil.Time
{

    /// <summary>
    /// 描 述：UnixTime
    /// </summary>
    public sealed class UnixTime
    {
        private static readonly int[] _days = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 };
        private static readonly int[] _leapDays = { -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };

        /// <summary>
        /// 将64位Unix时间（UTC）转换为表示本地时间中相同时间的DateTime实例
        /// </summary>
        /// <param name="time"></param>
        /// <returns>时间</returns>
        public static DateTime ToDateTime(long time)
        {
            const long secondsPerDay = 24 * 60 * 60;
            const long secondsPerYear = (365 * secondsPerDay);

            //
            // Determine the years since 1900. Start by ignoring leap years.
            //

            int tmp = (int)(time / secondsPerYear) + 70;
            time -= ((tmp - 70) * secondsPerYear);

            //
            // Correct for elapsed leap years
            //

            time -= (ElapsedLeapYears(tmp) * secondsPerDay);

            //
            // If we have underflowed the long range (i.e., if time < 0),
            // back up one year, adjusting the correction if necessary.
            //

            bool isLeapYear = false;

            if (time < 0)
            {
                time += secondsPerYear;
                tmp--;

                if (IsLeapYear(tmp))
                {
                    time += secondsPerDay;
                    isLeapYear = true;
                }
            }
            else if (IsLeapYear(tmp))
            {
                isLeapYear = true;
            }

            //
            // tmp now holds the value for year. time now holds the
            // number of elapsed seconds since the beginning of that year.
            //

            int year = tmp;

            //
            // Determine days since January 1 (0 - 365). 
            // Leave time with number of elapsed seconds in that day.
            //

            int yearDay = (int)(time / secondsPerDay);
            time -= yearDay * secondsPerDay;

            //
            // Determine months since January (0 - 11) and day of month (1 - 31).
            //

            int[] yearDaysByMonth = isLeapYear ? _leapDays : _days;
            int month = 1;
            while (yearDaysByMonth[month] < yearDay) month++;

            int mday = yearDay - yearDaysByMonth[month - 1];

            //
            // Determine hours since midnight (0 - 23), minutes after the hour
            // (0 - 59), and seconds after the minute (0 - 59).
            //

            int hour = (int)(time / 3600);
            time -= hour * 3600L;

            int min = (int)(time / 60);
            int sec = (int)(time - (min) * 60);

            //
            // Finally construct a DateTime instance in UTC from the various 
            // components and then return it adjusted to local time.
            // Note that this could throw an ArgumentException or 
            // ArgumentOutOfRangeException, which is fine by us and we'll
            // let it propagate.
            //

            return (new DateTime(year + 1900, month, mday, hour, min, sec)).ToLocalTime();
        }

        /// <summary>
        /// 时间返回秒数
        /// </summary>
        /// <param name="time">时间</param>
        /// <returns>自1970, 1, 1来的时间秒数</returns>
        public static long ToInt64(DateTime time)
        {
            return (long)(time.ToUniversalTime() - new DateTime(1970, 1, 1)).TotalSeconds;
        }

        /// <summary>
        /// 确定一个给定的年份（以1900年以来的年数表示）是否为闰年
        /// </summary>
        /// <param name="y"></param>
        /// <returns>是否为闰年</returns>
        private static bool IsLeapYear(int y)
        {
            return (((y % 4 == 0) && (y % 100 != 0)) || ((y + 1900) % 400 == 0));
        }

        /// <summary>
        /// 获取时间搓
        /// </summary>
        /// <returns>时间搓</returns>
        public static long Ticks()
        {
            var responseTime = (DateTime.Now.Ticks - new DateTime(1970, 1, 1).Ticks) / 10000000 - 8 * 60 * 60;
            return responseTime;
        }

        /// <summary>
        /// 从1970年到（但不包括）指定年份的闰年数，表示为自1900年以来的年数
        /// </summary>
        /// <param name="y"></param>
        /// <returns>闰年数</returns>
        private static long ElapsedLeapYears(int y)
        {
            return (((y - 1) / 4) - ((y - 1) / 100) + ((y + 299) / 400) - /* Leap years 1900 - 1970 = */ 17);
        }

        private UnixTime()
        {
            throw new NotSupportedException();
        }
    }
}
